package com.kuding.oss;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.time.Duration;
import java.util.Arrays;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.entity.ContentType;
import org.springframework.beans.factory.annotation.Autowired;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.MatchMode;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PolicyConditions;
import com.aliyun.oss.model.PutObjectRequest;
import com.kuding.models.OssPolicy;
import com.kuding.props.AliYunOssProperty;

public class OssComponent {

	private final Log logger = LogFactory.getLog(getClass());

	@Autowired
	private AliYunOssProperty aliYunOssProperty;

	private final String jsonContentType = "application/json;charset=utf-8";

	public OssComponent(AliYunOssProperty aliYunOssProperty) {
		this.aliYunOssProperty = aliYunOssProperty;
	}

	public OssComponent() {
		super();
	}

	/**
	 * 获取一个ossclient
	 * 
	 * @return
	 */
	public OSS createOss() {
		String accessId = aliYunOssProperty.getAccessKeyId();
		String accessSecret = aliYunOssProperty.getSecret();
		String endPoint = aliYunOssProperty.getEndPoint();
		OSS oss = new OSSClientBuilder().build(endPoint, accessId, accessSecret);
		return oss;
	}

	/**
	 * 
	 * 获取有访问权限的路径地址
	 * 
	 * @param bucketName
	 * @param key
	 * @param expiration
	 * @return
	 */
	public String generatePresignedUrl(String bucketName, String key, Duration expiration) {
		OSS oss = createOss();
		long milisec = expiration.toMillis();
		Date date = new Date(System.currentTimeMillis() + milisec);
		URL url = oss.generatePresignedUrl(bucketName, key, date);
		return url.toString();
	}

	public String generatePresignedUrl(String url, Duration expiration) {
		try {
			url = URLDecoder.decode(url, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		String pattern = "^https?\\:\\/\\/([^\\.]+)\\.SSS\\/(.+)$".replace("SSS", aliYunOssProperty.getEndPoint());
		Matcher matcher = Pattern.compile(pattern).matcher(url);
		if (matcher.find()) {
			String bucket = matcher.group(1);
			String path = matcher.group(2);
			return generatePresignedUrl(bucket, path, expiration);
		} else
			logger.warn("无法匹配Url");
		return url;
	}

	public OssPolicy createOssPolicy() {
		return createOssPolicy("");

	}

	/**
	 * 创建一个临时策略，用于oss的使用,使用配置中默认的bucket
	 * 
	 * @param dir
	 * @return
	 */
	public OssPolicy createOssPolicy(String dir) {
		return createOssPolicy(dir, aliYunOssProperty.getDefaultBucket());
	}

	/**
	 * 创建一个临时策略，用于oss的使用,使用指定的bucket，需要保证有权限访问
	 * 
	 * @param dir
	 * @param bucket
	 * @return
	 */
	public OssPolicy createOssPolicy(String dir, String bucket) {
		dir = dir.trim().endsWith("/") ? dir : dir + "/";
		dir = dir.replaceFirst("^\\/", "");
		bucket = bucket == null ? aliYunOssProperty.getDefaultBucket() : bucket;
		OSS oss = createOss();
		try {
			Duration expire = aliYunOssProperty.getPolicyTimeout();
			String host = aliYunOssProperty.getMainUrl(bucket);
			PolicyConditions policyConds = new PolicyConditions();
			policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
			if (!"/".equals(dir))
				policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
			String policy = oss.generatePostPolicy(new Date(System.currentTimeMillis() + expire.toMillis()),
					policyConds);
			String signature = oss.calculatePostSignature(policy);
			policy = BinaryUtil.toBase64String(policy.getBytes(Charset.forName("UTF-8")));
			OssPolicy re = new OssPolicy(bucket, aliYunOssProperty.getAccessKeyId(), policy, signature, dir, host,
					expire);
			return re;
		} finally {
			oss.shutdown();
		}
	}

	/**
	 * 将json传入oss成为一个json请求
	 * 
	 * @param json
	 * @param path
	 */
	public String putJsonToOss(String json, String path) {
		return putJsonToOss(json, path, aliYunOssProperty.getDefaultBucket());
	}

	/**
	 * 将json传入oss成为一个json请求（指定bucket）
	 * 
	 * @param json
	 * @param path
	 * @param bucket
	 */
	public String putJsonToOss(String json, String path, String bucket) {
		OSS oss = createOss();
		try {
			ObjectMetadata metadata = new ObjectMetadata();
			metadata.setContentType(jsonContentType);
			metadata.setContentEncoding("UTF-8");
			oss.putObject(bucket, path, new ByteArrayInputStream(json.getBytes(Charset.forName("UTF-8"))), metadata);
			return genericUrl(bucket, path);
		} finally {
			oss.shutdown();
		}
	}

	/**
	 * 将一个字符串上传到oss
	 * 
	 * @param object
	 * @param contentType
	 * @param path
	 * @return
	 */
	public String putStringToOss(String object, ContentType contentType, String path) {
		return putStringToOss(object, contentType, path, aliYunOssProperty.getDefaultBucket());
	}

	/**
	 * 将一个字符串上传到oss
	 * 
	 * @param object
	 * @param contentType
	 * @param path
	 * @param bucket
	 * @return
	 */
	public String putStringToOss(String object, ContentType contentType, String path, String bucket) {
		ObjectMetadata metadata = new ObjectMetadata();
		metadata.setContentType(contentType.toString());
		metadata.setContentEncoding("UTF-8");
		return putStringToOss(object, path, bucket, metadata);
	}

	public String putStringToOss(String object, String path, String bucket, ObjectMetadata objectMetadata) {
		OSS oss = createOss();
		try {
			oss.putObject(bucket, path, new ByteArrayInputStream(object.getBytes(Charset.forName("UTF-8"))),
					objectMetadata);
			return genericUrl(bucket, path);
		} finally {
			oss.shutdown();
		}
	}

	public String putStringToOss(String object, String path, ObjectMetadata objectMetadata) {
		OSS oss = createOss();
		String bucket = aliYunOssProperty.getDefaultBucket();
		try {
			oss.putObject(bucket, path, new ByteArrayInputStream(object.getBytes(Charset.forName("UTF-8"))),
					objectMetadata);
			return genericUrl(bucket, path);
		} finally {
			oss.shutdown();
		}
	}

	/**
	 * 将文件上传至oss
	 * 
	 * @param file
	 * @param path
	 * @return
	 */
	public String putFileToOss(File file, String path) {
		return putFileToOss(file, aliYunOssProperty.getDefaultBucket(), path);
	}

	/**
	 * 
	 * 将文件上传至oss
	 * 
	 * @param file
	 * @param bucket
	 * @param path
	 * @return
	 */
	public String putFileToOss(File file, String bucket, String path) {
		OSS oss = createOss();
		try {
			String fullFilePath = createFileKey(path, file.getName());
			PutObjectRequest objectRequest = new PutObjectRequest(aliYunOssProperty.getDefaultBucket(), fullFilePath,
					file);
			oss.putObject(objectRequest);
			return genericUrl(bucket, fullFilePath);
		} finally {
			oss.shutdown();
		}
	}

	/**
	 * 
	 * 将文件上传至oss并设置过期时间
	 * 
	 * @param file
	 * @param path
	 * @param expiration
	 * @return
	 */
	public String putFileToOss(File file, String path, Duration expiration) {
		return putFileToOss(file, aliYunOssProperty.getDefaultBucket(), path, expiration);
	}

	/**
	 * 
	 * 将文件上传至oss并设置过期时间
	 * 
	 * @param file
	 * @param bucket
	 * @param path
	 * @param expiration
	 * @return
	 */
	public String putFileToOss(File file, String bucket, String path, Duration expiration) {
		OSS oss = createOss();
		try {
			ObjectMetadata metadata = new ObjectMetadata();
			metadata.setExpirationTime(new Date(System.currentTimeMillis() + expiration.toMillis()));
			String fullFilePath = createFileKey(path, file.getName());
			oss.putObject(bucket, fullFilePath, file, metadata);
			return genericUrl(bucket, fullFilePath);
		} finally {
			oss.shutdown();
		}
	}

	public String genericUrl(String bucket, String fullFilePath) {
		if (fullFilePath.startsWith("/"))
			fullFilePath = fullFilePath.replaceFirst("\\/", "");
		String host = aliYunOssProperty.getMainUrl(bucket);
		String url = String.format("%s%s", host,
				URLEncodedUtils.formatSegments(Arrays.asList(fullFilePath.split("\\/")), Charset.forName("UTF-8")));
		return url;
	}

	public String genericUrl(String fullFilePath) {
		return genericUrl(aliYunOssProperty.getDefaultBucket(), fullFilePath);
	}

	private String createFileKey(String path, String fileName) {
		String fullPath = FilenameUtils.normalize(FilenameUtils.concat(path, fileName), true);
		logger.debug(path + "/" + fileName + "->" + fullPath);
		return fullPath;
	}

	/**
	 * 删除一个oss对象
	 * 
	 * @param path
	 */
	public void deleteObject(String path) {
		deleteObject(path, aliYunOssProperty.getDefaultBucket());
	}

	/**
	 * 删除一个oss对象（指定bucket）
	 * 
	 * @param path
	 * @param bucket
	 */
	public void deleteObject(String path, String bucket) {
		OSS oss = createOss();
		try {
			oss.deleteObject(bucket, path);
		} finally {
			oss.shutdown();
		}
	}
}
